跳到主要内容

着色器语言

IdeaXR 所提供的着色语言是基于流行的 OpenGL 着色语言(GLSL)的简化。支持大多数数据类型和函数。有关着色器和 OpenGL 着色语言的总体介绍,请参考 The Book of Shaders

支持的数据类型

类型描述
voidVoid 数据类型,只对不返回任何内容的函数有用。
bool布尔数据类型,只能包含true 或者 false
bvec2布尔值的二分量向量。
bvec3布尔值的三分量向量。
bvec4布尔值的四分量向量。
int有符号标量整数。
ivec2有符号整数的二分量向量。
ivec3有符号整数的三分量向量。
ivec4有符号整数的四分量向量。
uint无符号标量整数,不能包含负数。
uvec2无符号整数的二分量向量。
uvec3无符号整数的三分量向量。
uvec4无符号整数的四分量向量。
float浮点标量
vec2浮点值的二分量向量
vec3浮点值的三分量向量
vec4浮点值的四分量向量
mat22x2 矩阵,按列的主要顺序。
mat33x3 矩阵,按列的主要顺序。
mat44x4 矩阵,按列的主要顺序。
sampler2D用于绑定2D纹理的采样器类型, 以浮点(float)形式读取。
isampler2D用于绑定2D纹理的采样器类型, 以有符号整型(int)形式读取。
usampler2D用于绑定2D纹理的采样器类型, 以无符号整型(uint)形式读取。
sampler2DArray用于绑定2D纹理数组的采样器类型, 以浮点数(float)形式读取。
isampler2DArray用于绑定2D纹理数组的采样器类型, 以有符号整数(int)形式读取。
usampler2DArray用于绑定2D纹理数组的采样器类型, 以无符号整数(uint)形式读取。
sampler3D用于绑定3D纹理的采样器类型, 以浮点(float)形式读取。
isampler3D用于绑定3D纹理的采样器类型, 以有符号整型(int)形式读取。
usampler3D用于绑定3D纹理的采样器类型, 以无符号整型(uint)形式读取。
samplerCube用于绑定Cubemaps的采样器类型, 以浮点(float)形式读取。

构建

接下来介绍一些常用类型的定义方法

向量

// 输入对应数量的标量
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// 也可以使用其他向量填充,或者向量+标量
vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// 整个向量使用同一个标量
vec4 a = vec4(0.0);

与glsl相同,向量类型的单个标量成员通过 "x" , "y" , "z" 和 "w" 成员访问。另外, 使用 "r" , "g" , "b" 和 "a" 也可以,而且是等效的。

vec4 a = vec4(0.0, 1.0, 2.0, 3.0);

float b = a.r; //a.r = a.x = 0

矩阵

构建矩阵类型需要与矩阵相同维度的向量. 你也可以使用 matx(float) 语法构建一个对角矩阵. 相应地, mat4(1.0) 是一个单位矩阵.

mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 m4 = mat4(1.0);

矩阵也可以由另一维的矩阵建立. 有两条规则. 如果一个较大的矩阵是由一个较小的矩阵构建的, 那么额外的行和列将被设置为它们在单位矩阵中的值. 如果一个较小的矩阵是由一个较大的矩阵构建的, 则使用较大矩阵的顶部和左矩阵.

对于矩阵,使用m[column][row]索引语法来访问每个标量,或者使用m[idx]来通过行索引访问向量。

mat4 m4 = mat4(1.0);
mat2 m2 = mat2(m4); //取m4的左上角2x2矩阵
float a = m4[1][1]; //a = 1.0

数组

数组是存放多个相同类型变量的容器。

局部数组

局部数组在函数中声明。

void fragment(){
float arr[3];
}

要访问数组元素,使用索引语法:

float arr[3];

arr[0] = 1.0 //setter

COLOR.r = arr[0] //getter

数组还存在一个length内置函数,用于获取数组的大小。

float arr[] = {1.0,2.0,3.0}

for(int i = 0; i < arr.length(); i++){
//...
}
注意

如果你使用一个低于0或大于数组大小的索引,着色器将崩溃并中断渲染。为了防止这种情况,请使用length()ifclamp()函数来确保索引在0和数组的长度之间。

常量

在变量声明前使用const关键字,可以使该变量成为不可变的,这意味着它不能被修改。常量必须在其声明时被初始化。

const vec2 a = vec2(0.0, 1.0);
vec2 b;

a = b; // invalid
b = a; // valid

常量可以在全局(任何函数之外)或局部(一个函数之内)进行声明。当你想在着色器中访问一个不需要的修改的值,你就可以在开头定义一个全局常量。它在所有着色器阶段共享,但是不能在着色器之外访问。

shader_type spatial

const float PI = 3.14159265358979323846;

运算符

以下是着色器中可用的运算符以及优先级列表:

优先级操作符
1 (最高)括号()
2单目运算符+, -, !, ~
3乘除法/, *, %
4加减法+, -
5逐位移位<<, >>
6关系比较<, >, <=, >=
7相等比较==, !=
8按位与&
9按位或^
10按位异或**
11逻辑与&&
12 (最低)逻辑或**

流控制

支持常见的流控制类型

// if and else
if (cond) {

} else {

}

// switch
switch(i) { // signed integer expression
case -1:
break;
case 0:
return; // break or return
case 1: // pass-through
case 2:
break;
//...
default: // optional
break;
}

// for loops
for (int i = 0; i < 10; i++) {

}

// while
while (true) {

}

// do while
do {

} while(true);

discard

片元和光照函数可以使用 discard 关键字. 如果使用, 则丢弃该片元并且不写入任何内容.

函数

函数的定义可以使用以下语法

ret_type func_name(args) {
return ret_type; // if returning a value
}

//具体的例子:

int sum2(int a, int b) {
return a + b;
}

如果你想调用一个函数,那么这个函数的定义必须在调用之前。

函数参数可以有特殊的限定符:

  • in : 表示参数仅用于读取(默认)。

  • out : 表示该参数只用于写入。

  • inout : 表示该参数以引用传递。

例如:

void sum2(int a, int b, inout int result) {
result = a + b;
}

Varying

要从顶点处理器函数往片元(或者光)处理器函数里发送数据,可以使用 varying。顶点处理器中的每一个图元顶点都是 varying 的,会为片元处理器中的每一个像素做插值。

shader_type spatial;

varying vec3 some_color;

void vertex() {
some_color = vec3(1.0);
}

void fragment() {
ALBEDO = some_color;
}

void light() {
DIFFUSE_LIGHT = some_color * 100;
}

也可以使用 varying 关键字将数据从片元处理器送往光处理器。

shader_type spatial;

varying vec3 some_light;

void fragment() {
some_light = ALBEDO * 100.0; // Make a shining light.
}

void light() {
DIFFUSE_LIGHT = some_light;
}

注意,在自定义函数或光处理器中是不能为 varying 赋值的。

shader_type spatial;

varying float test;

void foo() {
test = 0.0; // Error.
}

void vertex() {
test = 0.0;
}

void light() {
test = 0.0; // Error too.
}

##Uniform 将值传递给着色器。这些值对整个着色器来说是全局的,被称为 uniform。当一个着色器被分配给一个材质时,uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。

shader_type spatial;

uniform vec3 some_color;

你可以在编辑器中设置材质中的 uniform。

change in editor

或者你可以通过 IVScript 来设置它们:

material.set_shader_param("some_color", Vector3(1,1,1))
注意

set_shader_param 的第一个参数是着色器中的 uniform 名称。它必须与着色器中的 uniform 名称完全一致,否则将无法被识别。

除了 void 之外,任何 GLSL 类型都可以成为 IdeaXR 还提供了可选的着色器提示,以使编译器了解 uniform 是用来干什么的。

shader_type spatial;

uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0);

以下是完整的提示列表:

类型提示描述
vec4hint_color用作颜色
int, floathint_range(min, max[, step])用作范围(最小值/最大值/步长)
sampler2Dhint_albedo用作反照率颜色
sampler2Dhint_normal用作法线贴图
sampler2Dhint_white作为值,默认为白色
sampler2Dhint_black作为值,默认为黑色
sampler2Dhint_aniso作为 FlowMap,默认为右。

IVRScript 使用的变量类型与 GLSL 不同,所以当把变量从 IVRScript 传递到着色器时,IdeaXR 会自动转换类型。以下是相应类型的表格:

IVRScript类型GLSL类型
boolbool
intint
floatfloat
Vector2vec2
Vector3vec3
Colorvec4
Transform3Dmat4
Transform2Dmat4

Uniform 也可以分配默认值:

shader_type spatial;

uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : hint_color = vec4(1.0);

内置变量

有大量类似UVCOLORVERTEX的内置变量可用。具体有哪些变量可用取决于着色器的类型(spatialcanvas_item以及particle)和所在的函数(vertexfragment以及light)。可用的内置变量列表见对应的页面:

内置函数

函数描述
vec_type radians (vec_type degrees)将度数转换为弧度
vec_type degrees (vec_type radians)将弧度转换为度数
vec_type sin (vec_type x)正弦
vec_type cos (vec_type x)余弦
vec_type tan (vec_type x)正切
vec_type asin (vec_type x)反正弦
vec_type acos (vec_type x)反余弦
vec_type atan (vec_type y_over_x)反正切
vec_type atan (vec_type y, vec_type x)将向量转换为角度的反正切
vec_type sinh (vec_type x)双曲正弦
vec_type cosh (vec_type x)双曲余弦
vec_type tanh (vec_type x)双曲正切
vec_type asinh (vec_type x)反双曲正弦
vec_type acosh (vec_type x)反双曲余弦
vec_type atanh (vec_type x)反双曲正切
vec_type pow (vec_type x, vec_type y)幂(x < 0 或 x = 0 且 y <= 0 时未定义
vec_type exp (vec_type x)以e为底的指数
vec_type exp2 (vec_type x)以2为底的指数
vec_type log (vec_type x)自然对数
vec_type log2 (vec_type x)以2为底的对数
vec_type sqrt (vec_type x)平方根
vec_type inversesqrt (vec_type x)反平方根
vec_type abs (vec_type x), ivec_type abs (ivec_type x)绝对值
vec_type sign (vec_type x), ivec_type sign (ivec_type x)如果 x>0 则返回1,小于0则返回-1
vec_type floor (vec_type x)向下取整
vec_type round (vec_type x)四舍五入
vec_type roundEven (vec_type x)四舍五入到最接近的偶数
vec_type trunc (vec_type x)截断
vec_type ceil (vec_type x)向上取整
vec_type fract (vec_type x)取小数部分 (返回 x - floor(x))
vec_type mod (vec_type x, vec_type y) ,vec_type mod (vec_type x, float y)取余
vec_type modf (vec_type x, out vec_type i)返回 x 的小数部分,i为整数
vec_type min (vec_type a, vec_type b)返回a,b中的最小值
vec_type max (vec_type a, vec_type b)返回a,b中的最大值
vec_type clamp (vec_type x, vec_type min, vec_type max)将x的值限制在min和max之间
float mix (float a, float b, float c)线性插值
vec_type mix (vec_type a, vec_type b, float c)线性插值(标量系数)
vec_type mix (vec_type a, vec_type b, vec_type c)线性插值(向量系数)
vec_type mix (vec_type a, vec_type b, bvec_type c)线性插值(布尔向量选择)
vec_type step (vec_type a, vec_type b)b[i] < a[i] ? 0.0 : 1.0
vec_type step (float a, vec_type b)b[i] < a ? 0.0 : 1.0
vec_type smoothstep (vec_type a, vec_type b, vec_type c)用于求解两个值之间的样条插值
vec_type smoothstep (float a, float b, vec_type c)用于求解两个值之间的样条插值
bvec_type isnan (vec_type x)如果标量或向量分量是 NaN 则返回 true
bvec_type isinf (vec_type x)如果标量或向量分量是 INF 则返回 true
ivec_type floatBitsToInt (vec_type x)Float->Int 位复制,无转换
uvec_type floatBitsToUint (vec_type x)Float->UInt 位复制,无转换
vec_type intBitsToFloat (ivec_type x)Int-> Float 位复制,无转换
vec_type uintBitsToFloat (uvec_type x)UInt->Float 位复制,无转换
float length (vec_type x)计算向量长度
float distance (vec_type a, vec_type b)计算向量a,b之间的距离
float dot (vec_type a, vec_type b)向量点乘
vec3 cross (vec3 a, vec3 b)向量叉乘
vec_type normalize (vec_type x)标准化为单位长度
vec3 reflect (vec3 I, vec3 N)计算反射向量
vec3 refract (vec3 I, vec3 N, float eta)计算折射向量
vec_type faceforward (vec_type N, vec_type I, vec_type Nref)如果 dot(Nref, I) <0, 则返回N, 否则返回-N
mat_type matrixCompMult (mat_type x, mat_type y)矩阵分量乘法
mat_type outerProduct (vec_type column, vec_type row)矩阵外积
mat_type transpose (mat_type m)转置矩阵
float determinant (mat_type m)矩阵行列式
mat_type inverse (mat_type m)逆矩阵
bvec_type lessThan (vec_type x, vec_type y)执行两个向量的分量小于(<)比较
bvec_type greaterThan (vec_type x, vec_type y)执行两个向量的分量大于(>)比较
bvec_type lessThanEqual (vec_type x, vec_type y)执行两个向量的分量小于等于(<=)比较
bvec_type greaterThanEqual (vec_type x, vec_type y)执行两个向量的分量大于等于(>=)比较
bvec_type equal (vec_type x, vec_type y)执行两个向量的分量等于(==)比较
bvec_type notEqual (vec_type x, vec_type y)执行两个向量的分量不等于(!=)比较
bool any (bvec_type x)存在为true的组件时返回true
bool all (bvec_type x)所有组件都为true是返回true
bvec_type not (bvec_type x)反转布尔向量
vec_type dFdx (vec_type p)使用局部微分进行 x 的微分
vec_type dFdy (vec_type p)使用局部微分进行 y 的微分
vec_type fwidth (vec_type p)xy 的绝对导数之和